Чтение заголовков HTTP в контроллере REST Spring

Я пытаюсь читать заголовки HTTP в REST API на основе Spring. Я последовал за этим. Но я получаю эту ошибку:

Для класса java.lang.String не найдено ни одного устройства чтения тела сообщения,
ContentType: 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?

Ответ 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 делает это волшебство для вас;)

Ответ 2

Я приведу вам пример того, как я читаю заголовки REST для своих контроллеров. Мои контроллеры принимают приложение /json только как тип запроса, если у меня есть данные, которые необходимо прочитать. Я подозреваю, что ваша проблема в том, что у вас есть приложение/октет-поток, который Spring не знает, как обращаться.

Обычно мои контроллеры выглядят так:

@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 - это настраиваемый объект, возвращаемый каждым контроллером. Он содержит статический factory, позволяющий создавать новые экземпляры. Моя служба данных является стандартным классом обслуживания.

Магия происходит за кулисами, потому что вы работаете с JSON, вам нужно сказать Spring использовать Джексона для сопоставления объектов 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 и это то, что Джексон использует для фактического отображения вашего запроса от JSON в объект.

Я подозреваю, что вы получаете свое исключение, потому что вы не указали картограф, который может читать Octet-Stream в объект или что-то, что может Spring. Если вы пытаетесь выполнить загрузку файла, это совсем другое.

Итак, мой запрос, который отправляется на мой контроллер, выглядит примерно так: у него есть дополнительный заголовок dataId.

Если вы хотите изменить это на параметр запроса и использовать @RequestParam String dataId, чтобы прочитать идентификатор запроса, ваш запрос будет выглядеть примерно так:

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.