在Spring REST控制器中读取HTTP头信息

57

我正在尝试在基于Spring的REST API中读取HTTP头。我遵循了这个。但是我得到了这个错误:

没有找到适合类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: 在这种情况下,null 表示 HTTPHeader 为空。

如何从HTTP头部获取值?


您的实际请求正在以 application/octect-stream 的形式发送,Spring 无法将其反序列化为字符串数据类型。将其设置为 application/json(如果您在类路径上有 Jackson)或您期望消耗的任何内容,您可能会取得一些进展。您是否尝试将文件上传到 REST 控制器? - JamesENL
我正在使用Google Postman进行调用。我将内容类型设置为application/json,但仍然出现错误。 - Ashwani K
3个回答

109

您收到的错误似乎与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。 - Mário Fernandes

8
我将为您提供一个示例,演示我如何读取控制器的 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: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请求映射到对象的工具。

我怀疑您之所以会遇到异常,是因为您没有指定一个可以将八位字节流读入对象的映射器,或者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,就不应该有任何问题。


7

不要在每个方法中获取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();
    }
}

3
使用代码生成可以在不在swagger定义中声明头信息的情况下获取它们,这非常有用。谢谢。 - Vespucci75fr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接