In Swagger Java API, when I use a responsecontainer="List" (Or "Set") with a code=400, I am not getting the model of the response on Swagger-GUI. I am just getting Array[Object].
Here is the concrete case:
#CrossOrigin
#RestController
#RequestMapping(value = "/api")
#Loggable(prepend = true, trim = false)
public class ConfigResource {
private final ConfigResourceDelegate delegate;
#Inject
public ConfigResource(final ConfigResourceDelegate delegate) {
this.delegate = delegate;
}
#RequestMapping(
value = "/v1/config",
method = PUT,
consumes = APPLICATION_JSON_UTF8_VALUE,
produces = APPLICATION_JSON_UTF8_VALUE
)
#ApiResponses(value = {#ApiResponse(code=202,message = "ACCEPTED" ),
#ApiResponse(code=200,response = Rejection.class, responseContainer
= "Set", message = "BAD_REQUEST"),
#ApiResponse(code=500, message = "INTERNAL_SERVER_ERROR")})
public ResponseEntity<?> putConfig(final #RequestBody ConfigDto
configDto){
return delegate.putConfig(riskConfigDto);
}
}
Here is the Rejection Class:
public class Rejection {
private Long id;
private RejectionDTO rejection;
private String originMessage;
public Rejection() {
}
public Long getId() {
return id;
}
public RejectionDTO getRejection() {
return rejection;
}
public String getOriginMessage() {
return originMessage;
}
public void setId(Long id) {
this.id = id;
}
public void setRejection(RejectionDTO rejection) {
this.rejection = rejection;
}
public void setOriginMessage(String originMessage) {
this.originMessage = originMessage;
}
}
So normally i'am supposed to have this model between [] in the swagger UI. However, I am getting Array[Object]:
See screen capture
To make your example work, you need to change your return value from wildcard, ResponseEntity<?>, to a concrete class, ResponseEntity<List<Rejection>>. Also, you need to change responseContainer to a List from Set.
#RequestMapping(
value = "/v1/config",
method = PUT,
consumes = APPLICATION_JSON_UTF8_VALUE,
produces = APPLICATION_JSON_UTF8_VALUE
)
#ApiResponses(value = {#ApiResponse(code=202,message = "ACCEPTED" ),
#ApiResponse(code=200,response = Rejection.class, responseContainer
= "List", message = "BAD_REQUEST"),
#ApiResponse(code=500, message = "INTERNAL_SERVER_ERROR")})
public ResponseEntity<List<Rejection>> putConfig(final #RequestBody ConfigDto
configDto){
return delegate.putConfig(riskConfigDto);
}
Related
I am new to Spring boot so the question may sound silly. I want to insert a json object into database. But it is giving me an error like:
"Failed to evaluate Jackson deserialization for type".
On console, I am getting an error like:
Http 415 Unsupported Media type error with JSON
Here is my POJO class:
#Entity
#Table(name = "academics")
public class Academics {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private int id;
#Column(name = "adhaarcard")
private String adhaarCard;
#Column(name = "grade")
private List grades;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getAdhaarCard() {
return adhaarCard;
}
public void setAdhaarCard(String adhaarCard) {
this.adhaarCard = adhaarCard;
}
public List getGrades() {
return grades;
}
public void setGrades(List grades) {
this.grades = grades;
}
}
My controller function:
#RequestMapping(value="saveacademics",method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Object> saveAvademics(#RequestBody Academics academics) {
academicsService.save(academics);
URI location = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}")
.buildAndExpand(academics.getId()).toUri();
return ResponseEntity.created(location).build();
}
Angularjs Code:
$scope.saveAcademics = function() {
var adhaar = sessionStorage.getItem("emp-key");
var _data = {
"adhaarCard":adhaar,
"grades": {
"graduation":
{ "ssc": "SSC", "hsc": "HSC", "quOne": $scope.qone
},
"specialization":
{ 'ssc': "N.A", 'hsc': $scope.hscSpl, 'qoneSpl': $scope.qoneSpl},
"grade":
{ 'ssc': $scope.sscGrade, 'hsc': $scope.hscGrade, 'qoneGrade': $scope.qoneGrade},
"university":
{ 'ssc': $scope.sscUni, 'hsc': $scope.hscUni, 'qoneUni': $scope.qoneUni},
"year":
{ 'ssc': $scope.sscYear, 'hsc': $scope.hscYear, 'qoneYear': $scope.qoneYear}
}
};
console.log(_data);
$http({
url: 'saveacademics',
method: "POST",
data: JSON.stringify(_data)
})
.then(function(response) {
alert("Success");
},
function(response) { // optional
alert("Error Occoured.");
});
}
Try MediaType.APPLICATION_JSON instead on MediaType.APPLICATION_JSON_VALUE
#RequestMapping(value="saveacademics",method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON)
MediaType.APPLICATION_JSON is a "public constant media type for application/json", whereas MediaType.APPLICATION_JSON_VALUE is a "String equivalent of MediaType.APPLICATION_JSON".
Json that you generate from angular js and your Java pojo are not matching.
Better you can reformat json as below
{ adhaarCard: "12", grades : [
{ university: "univ name", specialization: "sadd", grade: 83, graduation:"SSC", year: 2007 },
{ university: "univ name", specialization: "sadd", grade: 67, graduation:"HSC", year: 2009 }
]
}
And mapping PoJo class as
#Column(name = "adhaarcard")
private String adhaarCard;
#Column(name = "grade")
private List<Grades> grades ;
Grades.java
private String university;
private String specialization;
private int grade;
private Sting graduation;
private int year;
Add Column mapping and getter, setters.
Tip:
Instead of #RequestMapping you can use #PostMapping which is a shorthand. So you wont get confused with media type, it take application/json as default type.
#RequestMapping(value="saveacademics",method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
replace with
#PostMapping("saveacademics")
I have this code:
#GetMapping("/notes/{id}")
public ResponseEntity<Note> getNoteById(#PathVariable(value = "id") Long id) {
Note note = noteRepository.findOne(id);
if(note == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok().body(note);
}
So this method just finds information by the sent id.
The method noteRepository.findOne() accepts only Long or class extends org.springframework.data.domain.Example
I want to retrive data by my own variable "secretkey" (String). How can I do this?
Use this method :
#GetMapping("/notes/byKey/{secretKey}")
public ResponseEntity<Note> getNoteById(#PathVariable(value = "secretKey") String secretKey) {
Note note = noteRepository.findBySecretKey(secretKey);
if(note == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok().body(note);
}
in your NoteRepositoryclass add this method:
Note findBySecretKey(String secretKey);
note that I also changed the request path.
Assume that you are using Spring Data:
#Entity
#Table(name="note")
public class Note {
#Id
#GeneratedAuto
private Long id;
#Column(name="secretKey")
private String secretKey;
// getters, setters
}
//Repository
public interface NoteRepository extends JpaRepository<Note, Long> {
List<Note> findBySecretKey(String secretKey);
}
// Controller
#GetMapping(path = "/notes/secretKey/{secretKey}", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Note> getNoteBySecretKey(
#PathVariable(value = "secretKey") String secretKey) {
Note note = noteRepository.findBySecretKey(secretKey);
if(note == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok().body(note);
}
I need to expose two different set of values from my model, so i implemented 2 views
public class Views {
public static class Small{ }
public static class Large extends Small { }
}
Then, in my model i put (all other fields are annotated with JSONIgnore
#JsonView(Views.Small.class)
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id_posto", unique = true, nullable = false)
public int getIdPosto() {
return this.idPosto;
}
public void setIdPosto(int idPosto) {
this.idPosto = idPosto;
}
#JsonView(Views.Large.class)
#NotNull
#Column(name = "nome_posto_park")
public String getNomePosto() {
return this.nomePosto;
}
public void setNomePosto(String nomePosto) {
this.nomePosto = nomePosto;
}
On my Controllers I have 2 methods:
#RequestMapping(value = "/spots", method = RequestMethod.GET)
public ResponseEntity<Posto> getSpotStatus(#RequestParam(value = "idPosto") int idPosto,
#RequestParam(value = "occupied") boolean occupied) {
Posto posto = postoService.findByIdPosto(idPosto);
ObjectMapper mapper = new ObjectMapper();
mapper.disable(MapperFeature.DEFAULT_VIEW_INCLUSION);
mapper.setConfig(mapper.getSerializationConfig()
.withView(Views.Small.class));
mapper.convertValue(posto, JsonNode.class);
return new ResponseEntity<Posto>(posto, HttpStatus.OK);
and
#RequestMapping(value="/spot", method = RequestMethod.GET)
public ResponseEntity<List<Posto>> getSpotList(#RequestParam (value = "idPiano") int idPiano){
Piano piano = pianoService.findById(idPiano);
List<Posto> posti = postoService.showSpotsByFloor(-1, piano);
ObjectMapper mapper = new ObjectMapper();
mapper.disable(MapperFeature.DEFAULT_VIEW_INCLUSION);
mapper.setConfig(mapper.getSerializationConfig()
.withView(Views.Large.class));
mapper.convertValue(posti, JsonNode.class);
return new ResponseEntity<List<Posto>>(posti, HttpStatus.OK);
}
Che result is the same... (obviously the first is a single Posto and the second a List but all the fields from the model are serialized....
What I'm doing wrong when using views?
You need define produces and consume with agree view and annotation with #ResponseBody
Example: Change your needed
#Produces(value = { MediaType.APPLICATION_JSON_VALUE })
#Consumes(value = { MediaType.APPLICATION_JSON_VALUE })
public #ResponseBody public ResponseEntity<List<Posto>> getSpotList(...
//when request, put your client agree request view
protected HttpEntity<T> headers()
{
final HttpHeaders headers = new HttpHeaders();
headers.set("Accept", MediaType.APPLICATION_JSON_VALUE);
headers.set("application", MediaType.APPLICATION_JSON_VALUE);
// T define your type here
return new HttpEntity<T>(headers);
}
Your problem is that Spring instantiates its own Jackson ObjectMapper when the ApplicationContext starts.
You'll have to autowire the Spring managed ObjectMapper and configure that instance instead of creating your own with new.
private final ObjectMapper mapper;
public MyController(ObjectMapper mapper) {
this.mapper = mapper;
}
Dear spring Java professionals
please help me out in this :
I have a custom service in spring and I dont have any errors on my wildfly server when i run it . but when I do the below update request i am getting 400 bad request though im sending the format as specified in my controller
inside my controller :
#RequestMapping(value = "/user/updatefilters/{Id}", method = RequestMethod.POST)
public Response updateFilter(#PathVariable("Id") Long Id, #RequestBody #Valid Filter Filter) {
FilterService.updateFilter(Id, Filter);
HashMap<String, Object> response = new HashMap<>();
response.put("messages", null);
response.put("success", Boolean.valueOf(true));
return Response.instance().friendlyName("filter-updated").object(response).statusCode(HttpStatus.OK);
}
inside my service file :
public void updateFilter(Long Id,Filter Filter) {
List<Filter> currentFilter = FilterRepo.getFilters(Id, Filter.getFilterId().longValue(),null);
currentFilter.get(0).setLabel(Filter.getLabel());
FilterRepo.save(currentFilter.get(0));
for (FilterField FilterField : Filter.getFilterFields()) {
FilterField currentFilterField = FilterFieldRepo.getFilterField(FilterField.getfieldId());
if (currentFilterField != null) {
currentFilterField.setfield(FilterField.getfield());
currentFilterField.setTypeId(FilterField.getTypeId());
FilterFieldRepo.save(currentFilterField);
}
}
}
inside my repository :
public List<Filter> getFilterList(Long Id, String type) {
List<Filter> FilterField = FilterFieldRepo.getFilterFields(Id,type);
return FilterField;
}
public void updateFilter(Long Id,Filter Filter) {
List<Filter> currentFilter = FilterRepo.getFilters(Id, Filter.getFilterId().longValue(),null);
currentFilter.get(0).setLabel(Filter.getLabel());
FilterRepo.save(currentFilter.get(0));
for (FilterField FilterField : Filter.getFilterFields()) {
FilterField currentFilterField = FilterFieldRepo.getFilterField(FilterField.getfieldId());
if (currentFilterField != null) {
currentFilterField.setfield(FilterField.getfield());
currentFilterField.setTypeId(FilterField.getTypeId());
FilterFieldRepo.save(currentFilterField);
}
}
}
Please note that inside my entity I added a transient list like this :
#Transient
private List<FilterField> filterFields;
updated :
this is my Filter class i generated the crud in netbeans but added the transuent list manually:
#Entity
#Table(schema="hitmeister",name = "filters")
#NamedQueries({
#NamedQuery(name = "Filter.findAll", query = "SELECT s FROM Filter s"),
#NamedQuery(name = "Filter.findByFilterId", query = "SELECT s FROM Filter s WHERE s.filterId = :filterId"),
#NamedQuery(name = "Filter.findById", query = "SELECT s FROM Filter s WHERE s.Id = :Id"),
#NamedQuery(name = "Filter.findByLabel", query = "SELECT s FROM Filter s WHERE s.label = :label"),
#NamedQuery(name = "Filter.findByInsertionDate", query = "SELECT s FROM Filter s WHERE s.insertionDate = :insertionDate"),
#NamedQuery(name = "Filter.findByIsActive", query = "SELECT s FROM Filter s WHERE s.isActive = :isActive"),
#NamedQuery(name = "Filter.findByType", query = "SELECT s FROM Filter s WHERE s.type = :type")})
public class Filter implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "filter_id")
private Integer filterId;
#Basic(optional = false)
#NotNull
#Column(name = "id")
private int Id;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 500)
#Column(name = "label")
private String label;
#Basic(optional = true)
#Column(name = "insertion_date")
#Temporal(TemporalType.TIMESTAMP)
private Date insertionDate;
#Column(name = "is_active")
private Boolean isActive;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 20)
#Column(name = "type")
private String type;
#Transient
private List<FilterField> filterFields;
public Filter() {
}
public Filter(Integer filterId) {
this.filterId = filterId;
}
public Filter(Integer filterId, int Id, String label, Date insertionDate, String type) {
this.filterId = filterId;
this.Id = Id;
this.label = label;
this.insertionDate = insertionDate;
this.type = type;
}
public Integer getFilterId() {
return filterId;
}
public void setFilterId(Integer filterId) {
this.filterId = filterId;
}
public int getId() {
return Id;
}
public void setuserId(int Id) {
this.userId = userId;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public Date getInsertionDate() {
return insertionDate;
}
public void setInsertionDate(Date insertionDate) {
this.insertionDate = insertionDate;
}
public Boolean getIsActive() {
return isActive;
}
public void setIsActive(Boolean isActive) {
this.isActive = isActive;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
#Override
public int hashCode() {
int hash = 0;
hash += (filterId != null ? filterId.hashCode() : 0);
return hash;
}
#Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof Filter)) {
return false;
}
Filter other = (Filter) object;
if ((this.filterId == null && other.filterId != null) || (this.filterId != null && !this.filterId.equals(other.filterId))) {
return false;
}
return true;
}
#Override
public String toString() {
return " Filter #"+ filterId ;
}
public List<FilterField> getFilterFields() {
return filterFields;
}
public void setFilterFields(List<FilterField> filterFields) {
this.filterFields = filterFields;
}
}
If you need my entity code i can post it as well
Thanks In advance !
My first recommendation: (OP tried and it didn't work, she was sending POST request)
Change your mapping as below and I think you should be fine. Request from browser address bar is a GET request.
As you can see below, HTTP 400 comes when server is unable to understand the request client is sending, and in your case you are sending GET but server has nothing for GET but for POST, so 400.
W3C HTTP 400
10.4.1 400 Bad Request
The request could not be understood by the server due to malformed
syntax. The client SHOULD NOT repeat the request without
modifications.
Code fix:
#RequestMapping(value = "/user/updatefilters/{Id}", method = RequestMethod.GET)
My second recommendation:
I am not Spring expert but here are my 2 cents you can try based on the JSON object you have provided and your Filter mapping - (1.) Change userId to Id, (2.) Have insertionDate as NULL, instead of an empty string.
Make sure your JSON string variables are mapped case-sensitively with your Filter class mapping, and their values are compatible with reference types.
Either your request format is not what Spring expects, or one of the Filter validations is failing. Add a org.springframework.validation.Errors argument and dump the values to find out what validations failed.
public Response updateFilter(#PathVariable("Id") Long Id, #RequestBody #Valid Filter Filter, Errors filterErrors) {
You can sniff the actual traffic using curl or a network monitoring tool to make sure the HTTP transaction is really what you think it is.
EDIT: Having looked at the JSON in one of your comments, I think this is going to turn out to be upper/lower case in your JSON field names. Either change "Id" to "id" and "FilterId" to "filterId", or annotate the Filter fields with #XmlElement(name = "Id") and #XmlElement(name = "FilterId"). Java Bean property names are case sensitive.
EDIT 2: Filter.setuserId(int Id) is broken as well. You need a setId() method for deserializing the bean, and you need to change the method so it stores the passed argument instead of just setting userId to itself.
I am having a problem marshalling a RequestBody when the parent class has a namespace.
Class:
#XmlRootElement(name = "blah")
public class Test {
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
XML:
<blah>
<id>23333</id>
</blah>
Code:
#RequestMapping( value = "/blah", method = RequestMethod.POST, consumes = { MediaType.TEXT_XML_VALUE }, produces = { MediaType.TEXT_XML_VALUE})
public String getBlah( #RequestBody Test request ) throws Exception
{
assert(null != request.getId());
return "blah";
}
This works fine. However, if I use #XmlRootElement(name = "blah", namespace="home") on the class, and <blah xmlns="home"> in the request, the Test class constructs, but it's ID value is never set.
I'm at a loss.
Before public void setId method add annotation #XmlElement