简体   繁体   中英

HttpMediaTypeNotAcceptableException when returning POJO as JSON

I'm getting this problem : HttpMediaTypeNotAcceptableException when trying to return Cart object as JSON. What is weird, json mapping is working, as I can see here: json working

It's not working for Cart obviously, I put all the getters in the Cart class, but still have the same problem. Here is my controller method:

@RequestMapping(value="/{cartId}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public @ResponseBody Cart read(@PathVariable String cartId) {
    return cartService.read(cartId);
}

Here are dependencies, that I put in pom.xml:

<dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-core</artifactId>
        <version>2.8.5</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.8.5</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-annotations</artifactId>
        <version>2.8.5</version>
    </dependency>

Here is my Cart class:

@Getter
@Setter
@EqualsAndHashCode
public class Cart implements Serializable {

private static final long serialVersionUID = -4045729241960416615L;

private String cartId;
private Map<String,CartItem> cartItems;
private BigDecimal grandTotal;

public Cart() {
    cartItems = new HashMap<String, CartItem>();
    grandTotal = new BigDecimal(0);
}

public Cart(String cartId) {
    this();
    this.cartId = cartId;
}


public void addCartItem(CartItem item) {

    String productId = item.getProduct().getId();

    if(cartItems.containsKey(productId)) {

        CartItem existingCartItem = cartItems.get(productId);
        existingCartItem.setQuantity(existingCartItem.getQuantity()+ item.getQuantity());
        cartItems.put(productId, existingCartItem);

    }
    else {
        cartItems.put(productId, item);
    }
    updateGrandTotal();
}

public void removeCartItem(CartItem item) {

    String productId = item.getProduct().getId();
    cartItems.remove(productId);
    updateGrandTotal();
}

public void updateGrandTotal() {

    grandTotal= new BigDecimal(0);
    for(CartItem item : cartItems.values()){
        grandTotal = grandTotal.add(item.getTotalPrice());
    }

}

}

Here is my configuration

@Configuration
@EnableWebMvc
@ComponentScan("pl.edu.pwr")
public class WebConfig extends WebMvcConfigurerAdapter {


public static final String RESOURCES_LOCATION = "/resources/";
public static final String RESOURCES_HANDLER = RESOURCES_LOCATION + "**";

@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(performanceMonitorInterceptor());
    registry.addInterceptor(auditingInterceptor());
    registry.addInterceptor(promoCodeInterceptor());
}


@Bean
PromoCodeInterceptor promoCodeInterceptor() {
    return new PromoCodeInterceptor("OF3RTA", "invalidPromoCode", "products");
}

@Bean
AuditingInterceptor auditingInterceptor() {
    return new AuditingInterceptor();
}

@Bean
PerformanceMonitorInterceptor performanceMonitorInterceptor() {
    return new PerformanceMonitorInterceptor();
}

/*
 * Configure ContentNegotiationManager
 */
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
    configurer.ignoreAcceptHeader(true).defaultContentType(
            MediaType.TEXT_HTML);
}

/*
 * Configure ContentNegotiatingViewResolver
 */
@Bean
public ViewResolver contentNegotiatingViewResolver(ContentNegotiationManager manager) {
    ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
    resolver.setContentNegotiationManager(manager);

    // Define all possible view resolvers
    List<ViewResolver> resolvers = new ArrayList<>();

    resolvers.add(jaxb2MarshallingXmlViewResolver());
    resolvers.add(jsonViewResolver());
    resolvers.add(jspViewResolver());

    resolver.setViewResolvers(resolvers);
    return resolver;
}

@Bean(name = "filterMultipartResolver")
public CommonsMultipartResolver filterMultipartResolver() {
    return new CommonsMultipartResolver();
}

@Bean
public ViewResolver viewResolver() {
    InternalResourceViewResolver resolver = new InternalResourceViewResolver();
    resolver.setPrefix("/WEB-INF/views/");
    resolver.setSuffix(".jsp");
    resolver.setExposeContextBeansAsAttributes(true);
    return resolver;
}

/*
* Configure View resolver to provide XML output Uses JAXB2 marshaller to
* marshall/unmarshall POJO's (with JAXB annotations) to XML
*/
@Bean
public ViewResolver jaxb2MarshallingXmlViewResolver() {
    Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
    marshaller.setClassesToBeBound(Product.class);
    return new Jaxb2MarshallingXmlViewResolver(marshaller);
}

/*
 * Configure View resolver to provide JSON output using JACKSON library to
 * convert object in JSON format.
 */
@Bean
public ViewResolver jsonViewResolver() {
    return new JsonViewResolver();
}


/*
 * Configure View resolver to provide HTML output This is the default format
 * in absence of any type suffix.
 */
@Bean
public ViewResolver jspViewResolver() {
    InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
    viewResolver.setViewClass(JstlView.class);
    viewResolver.setPrefix("/WEB-INF/views/");
    viewResolver.setSuffix(".jsp");
    return viewResolver;
}

@Bean
public CharacterEncodingFilter characterEncodingFilter() {
    CharacterEncodingFilter filter = new CharacterEncodingFilter();
    filter.setEncoding("UTF-8");
    filter.setForceEncoding(true);
    return filter;
}

@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
    configurer.enable();
}

@Override
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
    registry.addResourceHandler(RESOURCES_HANDLER).addResourceLocations(RESOURCES_LOCATION);
}

@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
    UrlPathHelper urlPathHelper = new UrlPathHelper();
    urlPathHelper.setRemoveSemicolonContent(false);
    configurer.setUrlPathHelper(urlPathHelper);
}

}

Here is POJO that works:

@Data
@EqualsAndHashCode
@ToString
@XmlRootElement(name="product")
@Getter
@Setter
public class Product {
@Pattern(regexp = "P[0-9]+", message = "Błędny indentyfikator produktu")
@ProductId
private String id;
@Size(min=4, max=50, message="Błędna nazwa produktu. Powinna mieć od {min} do {max} znaków")
private String name;
@Min(value=0, message="Błędna cena produktu. Cena nie może być ujemna.")
@Digits(integer = 8, fraction = 2, message="Błędna cena produktu. Cena powinna składać się z 8 " +
        "liczb reprezentujących część całkowitą i 2 cyfr reprezentujących część ułamkową")
@NotNull(message = "Błędna cena produktu. Cena nie może być pusta.")
private BigDecimal unitPrice;
private String description;
private String manufacturer;
@NotNull(message = "Kategoria nie może być pusta")
@Category
private String category;
@Min(value=0, message="Liczba przedmiotów musi być większa lub równa 0.")
private long unitsInStock;
private long unitsInOrder;
private boolean discontinued;
private String condition;
@JsonIgnore
private MultipartFile productImage;

public Product() {
}

public Product(String id, String name, BigDecimal unitPrice) {
    this.id = id;
    this.name = name;
    this.unitPrice = unitPrice;
}

@XmlElement()
public String getId() {
    return id;
}

public void setId(String id) {
    this.id = id;
}

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

public BigDecimal getUnitPrice() {
    return unitPrice;
}

public void setUnitPrice(BigDecimal unitPrice) {
    this.unitPrice = unitPrice;
}

public String getDescription() {
    return description;
}

public void setDescription(String description) {
    this.description = description;
}

public String getManufacturer() {
    return manufacturer;
}

public void setManufacturer(String manufacturer) {
    this.manufacturer = manufacturer;
}

public String getCategory() {
    return category;
}

public void setCategory(String category) {
    this.category = category;
}

public long getUnitsInStock() {
    return unitsInStock;
}

public void setUnitsInStock(long unitsInStock) {
    this.unitsInStock = unitsInStock;
}

public long getUnitsInOrder() {
    return unitsInOrder;
}

public void setUnitsInOrder(long unitsInOrder) {
    this.unitsInOrder = unitsInOrder;
}

public boolean isDiscontinued() {
    return discontinued;
}

public void setDiscontinued(boolean discontinued) {
    this.discontinued = discontinued;
}

public String getCondition() {
    return condition;
}

public void setCondition(String condition) {
    this.condition = condition;
}


@XmlTransient
public MultipartFile getProductImage() {
    return productImage;
}

public void setProductImage(MultipartFile productImage) {
    this.productImage = productImage;
}

}

What is causing the problem?

UPDATE: I read that Content Negoatiating View Resolver has nothing to do with @ResponseBody. It is the Content Negoatiating View Resolver that has worked for Product.java POJO, and it's @ResponseBody that has not worked for Cart.java. I didn't know that they have nothing to do with each other. Maybe now someone will now what to do, in order to make @ResponseBody working (returning JSON).

I think you should try to specify the return type with produces = MediaType.APPLICATION_JSON_VALUE. so your code should be

 @RequestMapping(value="/{cartId}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
            public ResponseEntity<Cart> read(@PathVariable String cartId) {
                  Cart cart= cartService.read(cartId);
            return new ResponseEntity<>(cart, HttpStatus.OK);

            }

UPDATE: You don't have Getters and Setters for your Cart POJO, so you need:

getCartId setCartId getCartitems setCartitems getGrandTotal setGrandTotal

You can debate if Cart is a POJO indeed. In my believe you have to much business-logic in the object. My advise would be to create a CartServices or something like that, which manage all that logic. In that case you can keep Cart clean, as a real POJO. So only attributes and their getters and setters.

I believe these getters and setters are necessary for the serialization.

And have you tried:

@RequestMapping(value="/{cartId}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public @ResponseBody Cart read(@PathVariable String cartId) {
    return cartService.read(cartId);
}

UPDATE : I found a solution. What I had to do is deleting contentNegotiatingViewResolver and everything that was related to that. After I did it, everything started working.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM