У меня есть API ReST для приложения со всеми контроллерами, найденными в /api/
, все они возвращают ответы JSON с помощью @ControllerAdvice
, который обрабатывает все исключения для сопоставления с форматированными результатами JSON.
Это отлично работает с spring 4.0 @ControllerAdvice
теперь поддерживает сопоставление по аннотации. Я не могу решить, как вернуть результат JSON для ответов 401 - Unauthenticated и 400 - Bad Request.
Вместо этого spring просто возвращает ответ на контейнер (tomcat), который отображает это как HTML. Как я могу перехватить это и отобразить результат JSON, используя тот же метод, что и мой @ControllerAdvice
.
security.xml
<bean id="xBasicAuthenticationEntryPoint"
class="com.example.security.XBasicAuthenticationEntryPoint">
<property name="realmName" value="com.example.unite"/>
</bean>
<security:http pattern="/api/**"
create-session="never"
use-expressions="true">
<security:http-basic entry-point-ref="xBasicAuthenticationEntryPoint"/>
<security:session-management />
<security:intercept-url pattern="/api/**" access="isAuthenticated()"/>
</security:http>
XBasicAuthenticationEntryPoint
public class XBasicAuthenticationEntryPoint extends BasicAuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException authException)
throws IOException, ServletException {
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED,
authException.getMessage());
}
}
Я могу решить 401, используя BasicAuthenticationEntryPoint
для записи непосредственно в выходной поток, но я не уверен, что это лучший подход.
public class XBasicAuthenticationEntryPoint extends BasicAuthenticationEntryPoint {
private final ObjectMapper om;
@Autowired
public XBasicAuthenticationEntryPoint(ObjectMapper om) {
this.om = om;
}
@Override
public void commence(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException authException)
throws IOException, ServletException {
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
om.writeValue(httpResponse.getOutputStream(),
new ApiError(request.getRequestURI(),
HttpStatus.SC_UNAUTHORIZED,
"You must sign in or provide basic authentication."));
}
}
Мне еще предстоит выяснить, как обращаться с 400, хотя однажды я попробовал поймать все контроллеры, которые действительно работали, но казалось, что иногда это будет иметь странное противоречивое поведение с другими контроллерами, которые я не хочу пересматривать.
В моей реализации ControllerAdvice
есть catch, который, если spring выдает любое исключение для плохого запроса (400), он должен теоретически захватить его, но это не так:
@ControllerAdvice(annotations = {RestController.class})
public class ApiControllerAdvisor {
@ExceptionHandler(Throwable.class)
public ResponseEntity<ApiError> exception(Throwable exception,
WebRequest request,
HttpServletRequest req) {
ApiError err = new ApiError(req.getRequestURI(), exception);
return new ResponseEntity<>(err, err.getStatus());
}
}