Spring Boot+Thymeleaf调用NASA API时出现EL1007E异常求助
问题排查:Spring Boot + Thymeleaf 访问NASA API时出现SpEL空指针异常
运行JUnit测试能正常从NASA API获取JSON对象列表,但Spring Boot结合Thymeleaf展示数据时抛出异常:
org.springframework.expression.spel.SpelEvaluationException: EL1007E: Property or field 'photos' cannot be found on null
相关代码
Thymeleaf模板(index.html)
<html xmlns:th="http://thymeleaf.org"> <head> <title>Mars Rover API App</title> </head> <body> <h2>Welcome to the Mars Rover API Web App</h2> <img th:each="photo : ${roverData.photos}" th:src="${photo.imgSrc}" /> </body> </html>
响应实体类 RoverApiResponse
public class RoverApiResponse { private List<MarsPhoto> photos = new ArrayList<>(); public List<MarsPhoto> getPhotos() { return photos; } public void setPhotos(List<MarsPhoto> photos) { this.photos = photos; } @Override public String toString() { return "MarsRoverApiResponse [photos=" + photos + "]"; } }
API服务类 MarsRoverApiService
@Service public class MarsRoverApiService { private final String API_DEMO_LINK= "https://api.nasa.gov/mars-photos/api/v1/rovers/curiosity/photos?sol=2&api_key=DEMO_KEY"; public String getAPI_DEMO_LINK() { return API_DEMO_LINK; } public RoverApiResponse getRoverData() { RestTemplate restTemplate = new RestTemplate(); ResponseEntity<RoverApiResponse> response = restTemplate.getForEntity(getAPI_DEMO_LINK(), RoverApiResponse.class); return response.getBody(); } }
照片实体类 MarsPhoto
public class MarsPhoto { private Long id; private Integer sol; private Camera camera; @JsonProperty("img_src") private String imgSrc; @JsonProperty("earth_date") private Date earthDate; private Rover rover; // 省略getter和setter方法 @Override public String toString() { return "MarsPhoto [id=" + id + ", sol=" + sol + ", camera=" + camera + ", imgSrc=" + imgSrc + ", earthDate=" + earthDate + ", rover=" + rover + "]"; } }
控制器类 HomeController
@Controller public class HomeController { @Autowired private MarsRoverApiService apiService; @GetMapping("/") public String renderHomePage(ModelMap model) { RoverApiResponse roverData = apiService.getRoverData(); model.put("roverData", roverData); return "index"; } }
异常原因分析
异常明确说明模板中${roverData.photos}里的roverData为null,即apiService.getRoverData()返回了null。JUnit测试正常但Web环境异常,可能的核心原因:
- RestTemplate配置缺失:JUnit测试可能手动配置了Jackson消息转换器,但Web环境中每次新建的RestTemplate缺少JSON反序列化能力,导致无法解析API响应为实体类。
- 实体类映射不完整:
Camera、Rover等嵌套类未添加@JsonProperty注解,或字段类型与API响应不匹配,导致反序列化失败。 - 网络访问限制:Web环境存在代理、防火墙等限制,无法正常请求NASA API,返回null。
- API响应结构不匹配:实体类未覆盖API响应的所有必要字段,导致RestTemplate无法完成反序列化。
解决方法
1. 标准化RestTemplate配置
通过配置类创建全局RestTemplate实例,确保加载Jackson消息转换器:
@Configuration public class RestTemplateConfig { @Bean public RestTemplate restTemplate(ObjectMapper objectMapper) { RestTemplate restTemplate = new RestTemplate(); MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(); converter.setObjectMapper(objectMapper); restTemplate.getMessageConverters().add(converter); return restTemplate; } }
修改MarsRoverApiService注入RestTemplate,而非每次新建:
@Service public class MarsRoverApiService { private final String API_DEMO_LINK= "https://api.nasa.gov/mars-photos/api/v1/rovers/curiosity/photos?sol=2&api_key=DEMO_KEY"; private final RestTemplate restTemplate; public MarsRoverApiService(RestTemplate restTemplate) { this.restTemplate = restTemplate; } public RoverApiResponse getRoverData() { try { ResponseEntity<RoverApiResponse> response = restTemplate.getForEntity(getAPI_DEMO_LINK(), RoverApiResponse.class); // 打印响应信息用于排查 System.out.println("Response Status: " + response.getStatusCode()); return response.getBody(); } catch (Exception e) { e.printStackTrace(); return null; } } }
2. 完善实体类JSON映射
为嵌套类Camera、Rover添加@JsonProperty注解,确保字段与API响应匹配:
public class Camera { @JsonProperty("id") private Long id; @JsonProperty("name") private String name; // 补充其他字段及getter/setter } public class Rover { @JsonProperty("id") private Long id; @JsonProperty("name") private String name; // 补充其他字段及getter/setter }
对日期字段指定格式,避免解析失败:
@JsonProperty("earth_date") @JsonFormat(pattern = "yyyy-MM-dd") private Date earthDate;
3. 模板添加空值防护
在Thymeleaf模板中增加空值判断,避免直接访问null对象的属性:
<div th:if="${roverData != null}"> <img th:each="photo : ${roverData.photos}" th:src="${photo.imgSrc}" /> </div> <div th:if="${roverData == null}"> <p>加载火星车数据失败,请稍后重试。</p> </div>
4. 排查网络问题
通过捕获异常并打印日志,确认Web环境是否能正常访问NASA API,若存在网络限制需调整环境配置。
内容的提问来源于stack exchange,提问作者Misanty
相关产品推荐
相关产品推荐

