我有一个ReST API到一个应用程序,该应用程序中的所有控制器都在/API/
下找到,这些控制器都返回JSON响应,并带有一个@ControllerAdvice
,它处理所有异常以映射到JSON格式的结果。
从Spring 4.0开始,这非常有效@ControlllerAdvie
现在支持注释匹配。我无法解决的是如何为401-UnAuthated和400-Bad Request响应返回JSON结果。
相反,Spring只是将响应返回给容器(tomcat),该容器将其呈现为超文本标记语言。我如何截获它并使用我的@ControlllerAdvie
正在使用的相同技术呈现JSON结果。
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());
}
}
我可以通过使用BasicAuthentiationEntryPoint
直接写入输出流来解决401,但我不确定这是最好的方法。
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实现有一个包罗万象的功能,如果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());
}
}
您应该注意在ResponseEntity中设置错误代码,如下所示:
new ResponseEntity<String>(err, HttpStatus.UNAUTHORIZED);
我通过实现自己版本的HandlerExceptionResolver和子类化DefaultHandlerExceptionResolver解决了这个问题。解决这个问题需要一些时间,您必须覆盖大多数方法,尽管下面的内容正是我想要的。
首先,基础是创建一个实现。
public class CustomHandlerExceptionResolver extends DefaultHandlerExceptionResolver {
private final ObjectMapper om;
@Autowired
public CustomHandlerExceptionResolver(ObjectMapper om) {
this.om = om;
setOrder(HIGHEST_PRECEDENCE);
}
@Override
protected boolean shouldApplyTo(HttpServletRequest request, Object handler) {
return request.getServletPath().startsWith("/api");
}
}
现在在servlet上下文中注册它。
这现在没有什么不同,但将是第一个尝试的HandlerExceptionResolver,并将匹配以上下文路径为api的任何请求(注意:您可以将其设置为可配置参数)。
接下来,我们现在可以重写spring遇到错误的任何方法,我统计有15个。
我发现,如果我的方法最终没有处理导致尝试下一个异常解析程序的错误,我可以直接写入响应并返回空的ModelAndView对象,或者返回null。
作为处理发生请求绑定错误的情况的示例,我执行了以下操作:
@Override
protected ModelAndView handleServletRequestBindingException(ServletRequestBindingException ex, HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
ApiError ae = new ApiError(request.getRequestURI(), ex, HttpServletResponse.SC_BAD_REQUEST);
response.setStatus(ae.getStatusCode());
om.writeValue(response.getOutputStream(), ae);
return new ModelAndView();
}
这里唯一的缺点是,因为我自己在写流,所以我没有使用任何消息转换器来处理写响应,所以如果我想同时支持XML和JSON API,这是不可能的,幸运的是,我只对支持JSON感兴趣,但可以增强这一技术,使用视图解析器来确定在etc中呈现什么。
如果有人有更好的方法,我仍然有兴趣知道如何处理这个问题。
几周前,我发现自己也在问同样的问题——正如Dirk在评论中指出的那样,@ControllerAdvice只有在从控制器方法中抛出异常时才会生效,因此不可避免地无法捕获所有内容(我实际上是想解决404错误的JSON响应问题)。
我决定的解决方案,虽然不完全令人愉快(希望如果你得到一个更好的答案,我会改变我的方法)是处理Web.xml中的错误映射-我添加了以下内容,这将覆盖具有特定URL映射的tomcat默认错误页面:
<error-page>
<error-code>404</error-code>
<location>/errors/resourcenotfound</location>
</error-page>
<error-page>
<error-code>403</error-code>
<location>/errors/unauthorised</location>
</error-page>
<error-page>
<error-code>401</error-code>
<location>/errors/unauthorised</location>
</error-page>
现在,如果任何页面返回404,它将由我的错误控制器处理,如下所示:
@Controller
@RequestMapping("/errors")
public class ApplicationExceptionHandler {
@ResponseStatus(HttpStatus.NOT_FOUND)
@RequestMapping("resourcenotfound")
@ResponseBody
public JsonResponse resourceNotFound(HttpServletRequest request, Device device) throws Exception {
return new JsonResponse("ERROR", 404, "Resource Not Found");
}
@ResponseStatus(HttpStatus.UNAUTHORIZED)
@RequestMapping("unauthorised")
@ResponseBody
public JsonResponse unAuthorised(HttpServletRequest request, Device device) throws Exception {
return new JsonResponse("ERROR", 401, "Unauthorised Request");
}
}
这一切仍然感觉相当严峻——当然是对错误的全局处理——所以如果你并不总是希望404响应是json(如果你提供的是同一个应用程序的普通webapp),那么它就不那么好用了。但是就像我说的,这就是我决定要开始的,希望有更好的方法!
问题内容: 如何将Webmethod的值以JSON格式返回给客户端? 我要返回两个静态int值。 我是否需要使用这两个属性创建新对象并返回它? GetStatus()方法经常被调用,我不喜欢每次仅用于json格式创建一个特殊对象的想法。 在客户端,我在捕获返回值: 问题答案: 我只想带一个物体。它符合您的需求。如果您有两个返回值,则必须以结构化的方式将它们放在一起。
我使用Guzzle发出一个异步请求,返回JSON。呼叫工作正常,响应正常,但是: 如果我回应- 我需要使用 JSON 来验证来自服务的响应。我该怎么做?我已经浏览了文档,但我显然错过了一些东西。 本质上类似于将json getbody输出分配给$json: 感谢任何帮助问候
本文向大家介绍Springmvc如何返回xml及json格式数据,包括了Springmvc如何返回xml及json格式数据的使用技巧和注意事项,需要的朋友参考一下 问:@ResponseBody注解怎么指定返回xml 还是json 答:@RequestMapping 的produces 属性指定 produces = "application/xml" 或者 produces = "applica
我希望在spring boot中返回类似以下内容的json响应: 我的RestController如下所示 但我得到的反应是这样的
问题内容: 如何使用Jersey / JAX-RS框架以XML / JSON文档形式返回a并不是很明显。它已经支持s了,但是当涉及到s时,就没有了。即使将嵌入到包装器类中,XML模式中也没有类型。 关于如何在Jersey中将地图编组为XML / JSON文档的任何实用建议? 问题答案: 我知道它的回复很晚,但是我希望有一天它能对某人有所帮助:)我应用的最简单,最快的修复方法是 输出:{“ 1”:“
我有一个方法返回。代码如下类所示: 和作为错误。 如何解决此错误