Skip to content

Spring Boot Returning HTML Instead of JSON: How to Fix with @ResponseBody

Problem

When I called my Spring Boot REST endpoint, the frontend got an error:

Frontend error
SyntaxError: Unexpected token < in JSON at position 0

My endpoint returned a List&lt;User&gt; but the frontend received an HTML page instead of JSON data.

Environment

  • Spring Boot backend
  • Angular frontend
  • REST endpoint supposed to return JSON

What Happened?

I had a controller like this:

UserController.java (problem version)
@Controller
public class UserController {
@GetMapping("/users")
public List&lt;User&gt; getUsers() {
return userRepository.findAll();
}
}

I expected JSON output like:

Expected JSON
[
{"id": 1, "name": "John", "lastname": "Doe"},
{"id": 2, "name": "Jane", "lastname": "Smith"}
]

But the actual response was:

Actual response
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;Error&lt;/title&gt;
...

Why did Spring return HTML instead of JSON?

The Reason

The @Controller annotation tells Spring MVC that this class returns view names. Spring tries to find a template named “List<User>” or similar, and when it fails, it falls back to rendering an error page as HTML.

Without @ResponseBody, Spring treats your return value as a view reference, not as data to serialize.

Controller comparison diagram
│ │
▼ ▼
Returns view name Returns JSON data
│ │
▼ ▼
Spring finds template Jackson serializes
│ │
▼ ▼
HTML page JSON string

How to Solve It?

There are three approaches.

Solution 1: Add @ResponseBody to method

UserController.java (fixed with @ResponseBody)
@Controller
public class UserController {
@GetMapping("/users")
@ResponseBody
public List&lt;User&gt; getUsers() {
return userRepository.findAll();
}
}

The @ResponseBody annotation tells Spring to serialize the return value directly to the response body using Jackson.

Solution 2: Use @RestController (recommended)

UserController.java (fixed with @RestController)
@RestController
public class UserController {
@GetMapping("/users")
public List&lt;User&gt; getUsers() {
return userRepository.findAll();
}
}

@RestController combines @Controller + @ResponseBody. All methods automatically return JSON.

Solution 3: Explicit produces attribute

UserController.java (with produces)
@Controller
public class UserController {
@GetMapping(value = "/users", produces = "application/json")
@ResponseBody
public List&lt;User&gt; getUsers() {
return userRepository.findAll();
}
}

The produces attribute explicitly declares the response Content-Type.

Now the response is proper JSON:

Response headers
Content-Type: application/json;charset=UTF-8

Which Solution Should I Use?

Solution decision flowchart
└─────────────────────────┘
┌─────────────────────────┐
│ Use @RestController │
│ (cleanest approach) │
└─────────────────────────┘
┌─────────────────────────┐
│ Mixed controller? │
│ (some HTML, some JSON) │
└─────────────────────────┘
┌─────────────────────────┐
│ Use @Controller + │
│ @ResponseBody on JSON │
│ methods │
└─────────────────────────┘

For a pure REST API, @RestController is the cleanest choice.

Common Mistakes

Mistake 1: Using @Controller for REST

Wrong
@Controller // Returns view names by default
public class ApiController {
@GetMapping("/api/data")
public Data getData() { // Spring tries to find "Data" template
return dataService.getData();
}
}

Mistake 2: Double parsing in frontend

Angular HttpClient already parses JSON:

Don't double-parse
// Wrong: unnecessary double parse
data = JSON.parse(JSON.stringify(data));
// Right: Angular already parsed it
this.httpclient.get&lt;User[]&gt;('/api/users')
.subscribe(users => {
// users is already an array
});

Mistake 3: Not checking Content-Type

Always verify your server sends the right Content-Type:

Verify Content-Type
fetch('/api/users')
.then(response => {
const contentType = response.headers.get('content-type');
if (!contentType?.includes('application/json')) {
throw new Error('Expected JSON, got ' + contentType);
}
return response.json();
});

Summary

In this post, I showed how to fix Spring Boot returning HTML instead of JSON. The key point is: use @RestController or add @ResponseBody to your methods. The frontend error “Unexpected token <” is a symptom—the real fix is on the server side.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments