在 Spring REST 控制器中读取 HTTP 标头
Posted
技术标签:
【中文标题】在 Spring REST 控制器中读取 HTTP 标头【英文标题】:Reading HTTP headers in a Spring REST controller 【发布时间】:2015-03-28 08:32:22 【问题描述】:我正在尝试在基于 Spring 的 REST API 中读取 HTTP 标头。我关注了this。但是我收到了这个错误:
没有找到类 java.lang.String 的消息正文阅读器, 内容类型:application/octet-stream
我是 Java 和 Spring 的新手,所以无法弄清楚。
这就是我的调用的样子:
@WebService(serviceName = "common")
@Consumes( MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON )
@Produces( MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON )
public interface CommonApiService
@GET
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Produces(MediaType.APPLICATION_JSON)
@Path("/data")
public ResponseEntity<Data> getData(@RequestHeader(value="User-Agent") String userAgent, @DefaultValue ("") @QueryParam("ID") String id);
我试过@Context
:在这种情况下HTTPHeader是null
。
如何从 HTTP 标头中获取值?
【问题讨论】:
您的实际请求以application/octect-stream
的形式发送,Spring 无法将其反序列化回字符串数据类型。将其设置为application/json
(如果您在类路径上有杰克逊)或您期望使用的任何内容,您可能会到达某个地方。您是否有机会尝试将文件上传到您的 REST 控制器?
我正在使用 Google Postman 拨打电话。我将内容类型设置为 application/json 并仍然收到错误。
【参考方案1】:
您得到的错误似乎与 RequestHeader 无关。
而且您似乎将 Spring REST 服务与 JAX-RS 混淆了,您的方法签名应该类似于:
@RequestMapping(produces = "application/json", method = RequestMethod.GET, value = "data")
@ResponseBody
public ResponseEntity<Data> getData(@RequestHeader(value="User-Agent") String userAgent, @RequestParam(value = "ID", defaultValue = "") String id)
// your code goes here
你的 REST 类应该有如下注释:
@Controller
@RequestMapping("/rest/")
关于实际问题,另一种获取 HTTP 标头的方法是将 HttpServletRequest 插入到您的方法中,然后从那里获取所需的标头。
例子:
@RequestMapping(produces = "application/json", method = RequestMethod.GET, value = "data")
@ResponseBody
public ResponseEntity<Data> getData(HttpServletRequest request, @RequestParam(value = "ID", defaultValue = "") String id)
String userAgent = request.getHeader("user-agent");
不用担心 HttpServletRequest 的注入,因为 Spring 为您提供了魔法;)
【讨论】:
您仍在混合框架,您正在尝试使用 jax-rs 和 Spring rest。看看我的和@JamesMassey 的答案,以便使用 spring rest api。【参考方案2】:我将举一个例子来说明我是如何为我的控制器读取 REST 标头的。如果我有需要读取的数据,我的控制器只接受 application/json 作为请求类型。我怀疑你的问题是你有一个 Spring 不知道如何处理的 application/octet-stream。
通常我的控制器是这样的:
@Controller
public class FooController
@Autowired
private DataService dataService;
@RequestMapping(value="/foo/", method = RequestMethod.GET)
@ResponseBody
public ResponseEntity<Data> getData(@RequestHeader String dataId)
return ResponseEntity.newInstance(dataService.getData(dataId);
现在这里有很多代码在后台做一些事情,所以我会为你分解它。
ResponseEntity 是每个控制器返回的自定义对象。它包含一个允许创建新实例的静态工厂。我的数据服务是一个标准的服务类。
魔术发生在幕后,因为您使用的是 JSON,您需要告诉 Spring 使用 Jackson 来映射 HttpRequest 对象,以便它知道您在处理什么。
您可以通过在配置的 <mvc:annotation-driven>
块中指定它来做到这一点
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper" ref="objectMapper" />
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
ObjectMapper 只是 com.fasterxml.jackson.databind.ObjectMapper
的扩展,Jackson 使用它来实际将您的请求从 JSON 映射到对象。
我怀疑您遇到异常是因为您没有指定可以将 Octet-Stream 读入对象的映射器,或者 Spring 可以处理的东西。如果您尝试上传文件,那完全是另外一回事。
所以我发送到我的控制器的请求看起来像这样只是有一个名为dataId
的额外标头。
如果您想将其更改为请求参数并使用@RequestParam String dataId
从请求中读取 ID,您的请求将类似于以下内容:
contactId : "fooId"
此请求参数可以任意复杂。您可以将整个对象序列化为 JSON,将其作为请求参数发送,然后 Spring 会(使用 Jackson)将其序列化回可供您使用的 Java 对象。
控制器中的示例:
@RequestMapping(value = "/penguin Details/", method = RequestMethod.GET)
@ResponseBody
public DataProcessingResponseDTO<Pengin> getPenguinDetailsFromList(
@RequestParam DataProcessingRequestDTO jsonPenguinRequestDTO)
请求发送:
jsonPengiunRequestDTO:
"draw": 1,
"columns": [
"data":
"_": "toAddress",
"header": "toAddress"
,
"name": "toAddress",
"searchable": true,
"orderable": true,
"search":
"value": "",
"regex": false
,
"data":
"_": "fromAddress",
"header": "fromAddress"
,
"name": "fromAddress",
"searchable": true,
"orderable": true,
"search":
"value": "",
"regex": false
,
"data":
"_": "customerCampaignId",
"header": "customerCampaignId"
,
"name": "customerCampaignId",
"searchable": true,
"orderable": true,
"search":
"value": "",
"regex": false
,
"data":
"_": "penguinId",
"header": "penguinId"
,
"name": "penguinId",
"searchable": false,
"orderable": true,
"search":
"value": "",
"regex": false
,
"data":
"_": "validpenguin",
"header": "validpenguin"
,
"name": "validpenguin",
"searchable": true,
"orderable": true,
"search":
"value": "",
"regex": false
,
"data":
"_": "",
"header": ""
,
"name": "",
"searchable": false,
"orderable": false,
"search":
"value": "",
"regex": false
],
"order": [
"column": 0,
"dir": "asc"
],
"start": 0,
"length": 10,
"search":
"value": "",
"regex": false
,
"objectId": "30"
在提供给控制器供我使用之前,它会自动序列化回 DataProcessingRequestDTO 对象。
如您所见,这非常强大,允许您将数据从 JSON 序列化为对象,而无需编写任何代码。您可以对 @RequestParam
和 @RequestBody
执行此操作,这允许您分别访问参数或请求正文中的 JSON。
现在您有了一个具体的示例,一旦您将请求类型更改为 application/json
,您应该不会有任何问题。
【讨论】:
【参考方案3】:不是在每个方法中都使用HttpServletRequest
对象,而是通过构造函数自动连接来保持控制器的上下文。然后就可以从控制器的所有方法中访问了。
public class OAuth2ClientController
@Autowired
private OAuth2ClientService oAuth2ClientService;
private HttpServletRequest request;
@Autowired
public OAuth2ClientController(HttpServletRequest request)
this.request = request;
@RequestMapping(method = RequestMethod.POST)
public ResponseEntity<String> createClient(@RequestBody OAuth2Client client)
System.out.println(request.getRequestURI());
System.out.println(request.getHeader("Content-Type"));
return ResponseEntity.ok();
【讨论】:
在不使用代码生成在 swagger 定义中声明它们的情况下获取标题非常有用。谢谢以上是关于在 Spring REST 控制器中读取 HTTP 标头的主要内容,如果未能解决你的问题,请参考以下文章
REST API 在 Spring Boot 中删除 HTTP 请求
为啥 Spring Boot 中的 REST 控制器返回 HTTP 状态 404 – 未找到