记一次produces引发的404

本文探讨了一个服务突然返回404错误的问题,发现原因在于请求头Accept与服务设置的Content-Type不匹配,详细解析了Spring框架如何处理内容协商机制。

发现有个服务快应用过来的请求突然都404(no mapping found )了,但是自己用postman/curl调用或者浏览器都正常

开始怀疑是url中有乱码,加了个Filter, 这个是正常的,参数什么的也正常,按理是一样的

最后实在没招了,想到可能是Header的问题,如果打印出所有的Header,对比快应用与postman的请求

请求头Accept跟请求消息体Content-type居然都是application/x-protobuf, 

而自己的@RequestMapping又设置了produces="application/json"

这个确实不应该,其他服务都是走的pb协议,这个服务是普通的web服务走的json, 确认不是客户端加的,那是哪里,想到了网关,果不奇然,网关Accept/Content-type都是application/x-protobuf

两边匹配不上,

顺便翻了下spring的内容协商机制代码,这块是在AbstractMessageConverterMethodProcessor的writeWithMessageConverters中处理的

        } else {
            HttpServletRequest request = inputMessage.getServletRequest();
            //客户端可接受的
            List<MediaType> acceptableTypes = this.getAcceptableMediaTypes(request);
            //服务端所能提供的
            List<MediaType> producibleTypes = this.getProducibleMediaTypes(request, valueType, (Type)targetType);
            if (body != null && producibleTypes.isEmpty()) {
                throw new HttpMessageNotWritableException("No converter found for return value of type: " + valueType);
            }

            List<MediaType> mediaTypesToUse = new ArrayList();
            Iterator var15 = acceptableTypes.iterator();

            MediaType mediaType;
            while(var15.hasNext()) {
                mediaType = (MediaType)var15.next();
                Iterator var17 = producibleTypes.iterator();

                while(var17.hasNext()) {
                    MediaType producibleType = (MediaType)var17.next();
                    if (mediaType.isCompatibleWith(producibleType)) {
                        mediaTypesToUse.add(this.getMostSpecificMediaType(mediaType, producibleType));
                    }
                }
            }

            if (mediaTypesToUse.isEmpty()) {
                if (body != null) {
                    throw new HttpMediaTypeNotAcceptableException(producibleTypes);
                }

                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("No match for " + acceptableTypes + ", supported: " + producibleTypes);
                }

                return;
            }

            MediaType.sortBySpecificityAndQuality(mediaTypesToUse);
            var15 = mediaTypesToUse.iterator();

            while(var15.hasNext()) {
                mediaType = (MediaType)var15.next();
                if (mediaType.isConcrete()) {
                    selectedMediaType = mediaType;
                    break;
                }

                if (mediaType.isPresentIn(ALL_APPLICATION_MEDIA_TYPES)) {
                    selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;
                    break;
                }
            }

            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Using '" + selectedMediaType + "', given " + acceptableTypes + " and supported " + producibleTypes);
            }
        }

 

 

package com.api.hw.hssl; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.api.hw.utils.Result; import com.api.hw.utils.WebJsonUtils; import com.api.integration.web.OAuth2ServerAction; import weaver.conn.RecordSet; import weaver.general.BaseBean; import weaver.integration.logging.Logger; import weaver.integration.logging.LoggerFactory; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.Context; import java.util.HashMap; @Path("/hw/hssl") public class SyncOrderAPI { private Logger logger = LoggerFactory.getLogger(OAuth2ServerAction.class); @POST @Path("/SynOrders") @Produces("application/json") public Object getSyncOrderinfo(@Context HttpServletRequest request, @Context HttpServletResponse response){ this.logger.info("================getSyncOrderInfo start...==============================="); HashMap retMap = new HashMap(); Result result = null; try { String sql = "Insert INTO Order(orderType, start_datetime, end_datetime, discount, trainSeat,cabinType, actualPlaceOfDeparture, actualDestination, travelername,roomType, orderDate, orderAmount, vehicleType, originalOrderNo, universalType)values ('住宿','2025-03-01 14:00:00','2025-03-04 14:00:00' ,'0.9','二等座','经济舱','东莞','北京','张三','标间','2025-07-08','1000.00','经济型','202507060102','火车席位')"; RecordSet recordSet = getRecordSet(sql); JSONArray data = new JSONArray(); while(recordSet.next()){ JSONObject jsonObject = new JSONObject(); jsonObject.put("orderType",recordSet.getString("orderType")); jsonObject.put("start_datetime",recordSet.getString("start_datetime")); jsonObject.put("end_datetime",recordSet.getString("end_datetime")); jsonObject.put("discount",recordSet.getString("discount")); jsonObject.put("trainSeat",recordSet.getString("trainSeat")); jsonObject.put("cabinType",recordSet.getString("cabinType")); jsonObject.put("actualPlaceOfDeparture",recordSet.getString("actualPlaceOfDeparture")); jsonObject.put("actualDestination",recordSet.getString("actualDestination")); jsonObject.put("travelername",recordSet.getString("travelername")); jsonObject.put("roomType",recordSet.getString("roomType")); jsonObject.put("orderDate",recordSet.getString("orderDate")); jsonObject.put("orderAmount",recordSet.getString("orderAmount")); jsonObject.put("vehicleType",recordSet.getString("vehicleType")); jsonObject.put("originalOrderNo",recordSet.getString("originalOrderNo")); jsonObject.put("universalType",recordSet.getString("universalType")); data.add(jsonObject); } result = Result.build(200, "success!!!", data); return result; }catch (Exception e) { logger.error(e.getMessage()); result = Result.build(500, "=====error occured !" + e.getMessage(), retMap); } return result; } public static RecordSet getRecordSet(String sql) { RecordSet recordSet = new RecordSet(); BaseBean logInfo = new BaseBean(); try { recordSet.execute(sql); } catch (Exception e) { logInfo.writeLog("数据异常:" + e); } return recordSet; } }
最新发布
07-10
2025-05-29 02:32:31 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[POST /platform/chat/streamLLM],参数类型[json],参数:[{"model":"qwen","input":"讲的什么?","filePath":"http://fileeds.finchina.com:12001/wpspreview/mkplatform/2025/05/14/538a2f695f1a4aad99155327b59a43b1.html","fileGuid":"bcf418b1-b45f-46c2-a576-d47ac3e984c4","projectId":3651,"chatType":1}] Consume Time:83 ms 2025-05-29 02:32:31 Execute SQL:SELECT id,guid,file_name,source_path,target_path,original_path,state,public_state,memo,tmstamp,is_del,project_id,session_id,create_dept,create_by,create_time,update_by,update_time FROM mk_files WHERE (guid = 'bcf418b1-b45f-46c2-a576-d47ac3e984c4') Consume Time:17 ms 2025-05-29 02:32:32 Execute SQL:INSERT INTO mk_prompts ( id, guid, project_id, type, content, files_guid, model_type, create_dept, create_by, create_time, update_by, update_time ) VALUES ( 1927915933471264769, '3cd0828a-b265-4221-bbe1-09cc7ad88e7f', 3651, 0, '讲的什么?', 'bcf418b1-b45f-46c2-a576-d47ac3e984c4', 'qwen', 103, 1, '2025-05-29 02:32:32', 1, '2025-05-29 02:32:32' ) Consume Time:78 ms 2025-05-29 02:32:32 Execute SQL:INSERT INTO mk_prompts ( id, guid, project_id, type, files_guid, prompts_guid, model_type, create_dept, create_by, create_time, update_by, update_time ) VALUES ( 1927915933622259713, '9ddef2c6-f774-435e-b901-f4982f9bb0a0', 3651, 1, 'bcf418b1-b45f-46c2-a576-d47ac3e984c4', '3cd0828a-b265-4221-bbe1-09cc7ad88e7f', 'qwen', 103, 1, '2025-05-29 02:32:32', 1, '2025-05-29 02:32:32' ) 2025-05-29 02:33:04 [ForkJoinPool.commonPool-worker-1] WARN o.d.c.m.h.InjectionMetaObjectHandler - 自动注入警告 => 用户未登录 2025-05-29 02:33:04 [ForkJoinPool.commonPool-worker-1] ERROR o.d.p.controller.MkChatController - Error in streamLLM java.lang.RuntimeException: {"status": "ERROR","error_msg": "处理响应时发生错误:org.mybatis.spring.MyBatisSystemException"} at org.dromara.platform.service.impl.MkChatServiceImpl.lambda$requestLLMStream$2(MkChatServiceImpl.java:278) at java.base/java.util.concurrent.CompletableFuture$UniAccept.tryFire(CompletableFuture.java:718) at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:510) at java.base/java.util.concurrent.CompletableFuture.postFire(CompletableFuture.java:614) at java.base/java.util.concurrent.CompletableFuture$UniWhenComplete.tryFire(CompletableFuture.java:844) at java.base/java.util.concurrent.CompletableFuture$Completion.exec(CompletableFuture.java:483) at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:373) at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1182) at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1655) at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1622) at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:165) 这个是报错,以下是代码 @SaIgnore @ResponseBody @PostMapping(value = "/streamLLM", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public Flux<String> streamLLM(@RequestBody PromptBo promptBo) throws Exception { return chatService.getAnswerByFlux(promptBo) .onErrorResume(e -> { log.error("Error in streamLLM", e); return Flux.just(e.getMessage()); }); } @Override public Flux<String> getAnswerByFlux(PromptBo promptBo) throws Exception { String requestURL = LLMUrl; if(promptBo != null && promptBo.getModel() != null){ if(promptBo.getModel().equals("deepseek-v3")||promptBo.getModel().equals("deepseek-r1")||promptBo.getModel().equals("deepseek-r1-volc")){ requestURL = LLMUrlDS; } } String content; Long projectId = promptBo.getProjectId(); if (projectId == null){ return Flux.error(new RuntimeException("项目id不能为空")); } String questionGuid = UUID.randomUUID().toString(); MkPromptsBo question = new MkPromptsBo(questionGuid,projectId,0,promptBo.getModel()); //入库问题 MkPromptsBo answer = new MkPromptsBo(UUID.randomUUID().toString(),projectId,1,questionGuid,promptBo.getModel()); // 入库答案 MkPromptsBo think = new MkPromptsBo(UUID.randomUUID().toString(),projectId,2,questionGuid,promptBo.getModel()); // think if (promptBo.getChatType() == 1 || promptBo.getChatType() == 2){ // 全文问答或局部检索问答 MkFiles file = filesService.queryTargetPathByGuid(promptBo.getFileGuid()); String fileType = getFileExtension(file.getSourcePath(),file.getFileName()); R<String> r = getFileContent(fileType, file); if (r.getCode() == R.SUCCESS){ content = r.getData(); } else { return Flux.error(new RuntimeException("{\"status\": \"ERROR\",\"error_msg\": \"" + r.getMsg() + "\"}")); } } else if(promptBo.getChatType() == 0) { // 局部问答 content = promptBo.getContent()==null?"":promptBo.getContent(); } else{ return Flux.error(new RuntimeException("chatType参数有误")); } question.setFilesGuid(promptBo.getFileGuid()); question.setContent(promptBo.getInput()); answer.setFilesGuid(promptBo.getFileGuid()); think.setFilesGuid(promptBo.getFileGuid()); promptsService.insertByBo(question); promptsService.insertByBo(answer); if(promptBo.getModel().equals("deepseek-r1")||promptBo.getModel().equals("deepseek-r1-volc")){ promptsService.insertByBo(think); } // 向大模型提问 String promptStr = "You are a helpful assistant designed to output JSON."; PromptRequest requestModel = new PromptRequest(promptBo.getModel(),content,promptBo.getInput(),promptStr,true); // 当为局部检索问答时需要设置section_qa参数 if (promptBo.getChatType() == 2 ){ MkProjectsVo mkProjectsVo = projectsService.queryById(projectId); ObjectMapper objectMapper = new ObjectMapper(); List<String> keywords = objectMapper.readValue(mkProjectsVo.getKeywords(), new com.fasterxml.jackson.core.type.TypeReference<>() { }); requestModel.setSection_qa(new SectionQa(keywords,mkProjectsVo.getDirection(),mkProjectsVo.getOffsets())); } ObjectMapper mapper = new ObjectMapper(); String requestStr; try { requestStr = mapper.writeValueAsString(requestModel); } catch (JsonProcessingException e) { throw new RuntimeException(e); } return requestLLMStream(requestStr,promptBo,question,answer,think,requestURL); } private Flux<String> requestLLMStream(String requestStr,PromptBo promptBo,MkPromptsBo question,MkPromptsBo answer,MkPromptsBo think,String url){ HttpClient client = HttpClient.newHttpClient(); HttpRequest request; try { request = HttpRequest.newBuilder() .uri(new URI(url)) .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString(requestStr)) .build(); } catch (Exception e) { return Flux.create(sink -> { sink.next("{\"status\": \"ERROR\",\"error_msg\": \"构建大模型请求出现错误!\"}"); sink.complete(); }); } return Flux.create(sink -> { CompletableFuture<HttpResponse<InputStream>> responseFuture = client.sendAsync(request, HttpResponse.BodyHandlers.ofInputStream()); responseFuture .completeOnTimeout(null, 60, TimeUnit.SECONDS) // 60秒超时 .thenAccept(response -> { if (response == null) { sink.next("{\"status\": \"ERROR\",\"error_msg\": \"请求超时\"}"); sink.complete(); return; } int code = response.statusCode(); if (code != 200) { sink.next("{\"status\": \"ERROR\",\"error_msg\": \"请求出现问题,请联系管理员!状态码:"+code+"\"}"); sink.complete(); return; } String idMessage = String.format("{\"type\":\"ids\",\"questionId\":\"%s\",\"answerId\":\"%s\"}\n\n", question.getId(), answer.getId()); sink.next(idMessage); AtomicBoolean isClientDisconnected = new AtomicBoolean(false); sink.onCancel(() -> { isClientDisconnected.set(true); }); try (BufferedReader reader = new BufferedReader(new InputStreamReader(response.body(), StandardCharsets.UTF_8))) { String line; StringBuilder output = new StringBuilder(); StringBuilder thinkString = new StringBuilder(); ObjectMapper mapper2 = new ObjectMapper(); HashMap<String,String> processedLines = new HashMap<>(); // 用于录"model" while ((line = reader.readLine()) != null && !isClientDisconnected.get()) { if(!line.isBlank()){ line = line.replaceFirst("data:",""); if ("[DONE]".equals(line.trim())) { break; } processJsonLine(processedLines,line, output, thinkString,mapper2); } sink.next(line); } // 完整答案拼接完成后,一次性入库 String fullAnswer = output.toString(); answer.setContent(fullAnswer); answer.setModelType(processedLines.get("model")); if(processedLines.containsKey("page_range")&&!processedLines.get("page_range").equals("null")){ answer.setPageRange(processedLines.get("page_range")); } if(promptBo.getModel().equals("deepseek-r1")||promptBo.getModel().equals("deepseek-r1-volc")) { think.setContent(thinkString.toString()); think.setModelType(processedLines.get("model")); think.setPageRange(processedLines.get("page_range")); promptsService.updateByBo(think); } question.setModelType(processedLines.get("model")); promptsService.updateByBo(answer); promptsService.updateByBo(question); if (!isClientDisconnected.get()) { // 如果客户端没有断开连接,发送 [DONE] 信号 sink.next("[DONE]"); } sink.complete(); } catch (Exception e) { sink.error(new RuntimeException("{\"status\": \"ERROR\",\"error_msg\": \"处理响应时发生错误:"+e+"\"}")); } }) .exceptionally(e -> { if (e instanceof IOException) { sink.next("{\"status\": \"ERROR\",\"error_msg\": \"模型调用请求失败:"+ e.getMessage()+"\"}"); } else if (e instanceof InterruptedException) { sink.next("{\"status\": \"ERROR\",\"error_msg\": \"请求模型连接被中断\"}"); } else { sink.next("{\"status\": \"ERROR\",\"error_msg\": \"发生未知错误:"+ e.getMessage()+"\"}"); } sink.complete(); return null; }); }); } @Override public Boolean updateByBo(MkPromptsBo bo) { MkPrompts update = MapstructUtils.convert(bo, MkPrompts.class); validEntityBeforeSave(update); return baseMapper.updateById(update) > 0; } -- public.mk_prompts definition -- Drop table -- DROP TABLE public.mk_prompts; CREATE TABLE public.mk_prompts ( id bigserial NOT NULL, -- 流水号 guid varchar(50) NULL, project_id int8 NULL, -- 项目表的id "type" int4 NULL, -- 录类型 枚举值:0-问题;1-答案 "content" text NULL, -- type=0时为问题内容,type=1时为回答内容 files_guid varchar(50) NULL, -- 对应文件guid prompts_guid varchar(50) NULL, -- 对应问题的guid,type=1时有值 model_type varchar(200) NULL, -- 选择的模型 create_time date DEFAULT CURRENT_DATE NULL, -- 创建时间 update_time date DEFAULT CURRENT_DATE NULL, -- 更新时间 tmstamp varchar DEFAULT CURRENT_TIMESTAMP NULL, -- 时间戳 create_dept int8 NULL, -- 创建部门 create_by int8 NULL, -- 创建用户 update_by int8 NULL, -- 更新用户 page_range varchar(200) NULL, -- 关键词问答返回请求 {关键词: [1,2]} 比如:{"市净率":[0,1,2,3,4,5]} CONSTRAINT mk_prompts_pk PRIMARY KEY (id) ); -- Column comments COMMENT ON COLUMN public.mk_prompts.id IS '流水号'; COMMENT ON COLUMN public.mk_prompts.project_id IS '项目表的id'; COMMENT ON COLUMN public.mk_prompts."type" IS '录类型 枚举值:0-问题;1-答案'; COMMENT ON COLUMN public.mk_prompts."content" IS 'type=0时为问题内容,type=1时为回答内容'; COMMENT ON COLUMN public.mk_prompts.files_guid IS '对应文件guid'; COMMENT ON COLUMN public.mk_prompts.prompts_guid IS '对应问题的guid,type=1时有值'; COMMENT ON COLUMN public.mk_prompts.model_type IS '选择的模型'; COMMENT ON COLUMN public.mk_prompts.create_time IS '创建时间'; COMMENT ON COLUMN public.mk_prompts.update_time IS '更新时间'; COMMENT ON COLUMN public.mk_prompts.tmstamp IS '时间戳'; COMMENT ON COLUMN public.mk_prompts.create_dept IS '创建部门'; COMMENT ON COLUMN public.mk_prompts.create_by IS '创建用户'; COMMENT ON COLUMN public.mk_prompts.update_by IS '更新用户'; COMMENT ON COLUMN public.mk_prompts.page_range IS '关键词问答返回请求 {关键词: [1,2]} 比如:{"市净率":[0,1,2,3,4,5]}'; 我本地启动时这个接口不会出现这个错误,一旦部署到服务器上就出现了这个错误而且就这一个接口,真的是太奇怪了,帮我看看是什么问题?
05-30
The term **"produces"** is often used in technical contexts to describe the output or result generated by a system, process, algorithm, or device. Below is its definition and technical usage based on relevant fields: ### Definition of Produces In general terms, "produces" refers to creating, generating, or yielding something as an outcome from a given operation or procedure. In specific domains like computer science, finance, or engineering, it may have more precise meanings depending on context. For instance: - Within the scope of card acceptance devices described earlier[^1], "produces" could refer to actions such as generating transaction data after reading magnetic stripes or chips. - Regarding recurrent neural networks (RNNs)[^2], when discussing their functionality, one might say they produce predictions about future elements within sequences using accumulated states over time steps. Thus, technically speaking across various disciplines including but not limited to those mentioned above,"produce" implies delivering tangible results derived via computational processes involving inputs transformed into meaningful outputs according to predefined rulesets implemented either physically through hardware components(e.g.,card readers)or logically inside software algorithms(such as deep learning models). ```python def example_producing_function(input_data): processed_output = perform_operations_on_input(input_data) return processed_output # This function 'produces' some form of output based upon provided input. ``` §§Related Questions§§ 1. How does a card acceptor specifically generate authorization requests during transactions? 2. What types of outputs do Recurrent Neural Networks typically create while handling sequential datasets? 3. Can you provide examples where producing outcomes differ significantly between different machine-learning architectures compared against traditional methods? 4. Is there any distinction made regarding what constitutes production versus transformation under certain standards governing financial technologies? 5. When designing systems meant primarily for producing structured responses out unstructured sources how important becomes choice architecture utilized ?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值