9

尝试使用 Spring MVC 构建 RESTful Web 服务。

控制器应返回特定的 Java 类型,但响应主体必须是通用信封。如何才能做到这一点?

到目前为止,以下代码部分是我所拥有的:

控制器方法:

    @Controller
    @RequestMapping(value = "/mycontroller")
    public class MyController {

        public ServiceDetails getServiceDetails() {
             return new ServiceDetails("MyService");
        }
    }

响应信封:

    public class Response<T> {

        private String message;
        private T responseBody;

    }

ServiceDetails代码:

    public class ServiceDetails {

        private String serviceName;

        public ServiceDetails(String serviceName) {
            this.serviceName = serviceName;
        }
    }

对客户的预期最终响应应显示为:

   {

     "message" : "Operation OK"
     "responseBody" : {
                        "serviceName" : "MyService"
                      }

   }  
4

2 回答 2

0

默认情况下,Spring MVC 使用 org.springframework.http.converter.json.MappingJacksonHttpMessageConverter 通过 Jackson 对 JSON 进行序列化/反序列化。

我不确定这是否是个好主意,但解决问题的一种方法是扩展此类,并覆盖 writeInternal 方法:

public class CustomMappingJacksonHttpMessageConverter extends MappingJacksonHttpMessageConverter {

    @Override
    protected void writeInternal(Object object, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
        super.writeInternal(new Response(object, "Operation OK"), outputMessage);
    }
}

如果您使用 XML 配置,您可以像这样启用自定义转换器:

<mvc:annotation-driven>
    <mvc:message-converters>
        <bean class="path.to.CustomMappingJacksonHttpMessageConverter">
    </mvc:message-converters>
</mvc:annotation-driven>
于 2013-01-24T13:05:27.323 回答
0

您可以做的是MyRestController将结果包装成Response这样:

@Controller
@RequestMapping(value = "/mycontroller")
public class MyRestController {

    @Autowired
    private MyController myController;

    @RequestMapping(value = "/details")
    public @ResponseBody Response<ServiceDetails> getServiceDetails() {
         return new Response(myController.getServiceDetails(),"Operation OK");
    }
}

此解决方案使您的原始MyController代码独立于您的 REST 代码。看来您需要在类路径中包含 Jackson,以便 Spring 自动神奇地序列化为 JSON(有关详细信息,请参阅内容)

编辑

看来你需要更通用的东西......所以这里有一个建议。

@Controller
@RequestMapping(value = "/mycontroller")
public class MyGenericRestController {

    @Autowired
    private MyController myController;

    //this will match all "/myController/*"
    @RequestMapping(value = "/{operation}")
    public @ResponseBody Response getGenericOperation(String @PathVariable operation) {
          Method operationToInvoke = findMethodWithRequestMapping(operation);
          Object responseBody = null;
          try{
               responseBody = operationToInvoke.invoke(myController);
          }catch(Exception e){
               e.printStackTrace();
               return new Response(null,"operation failed");
          }
         return new Response(responseBody ,"Operation OK");
    }

    private Method findMethodWithRequestMapping(String operation){
         //TODO
         //This method will use reflection to find a method annotated
         //@RequestMapping(value=<operation>)
         //in myController
         return ...
    }
}

并保持原来的“myController”几乎原样:

@Controller
public class MyController {

    //this method is not expected to be called directly by spring MVC
    @RequestMapping(value = "/details")
    public ServiceDetails getServiceDetails() {
         return new ServiceDetails("MyService");
    }
}

主要问题:@RequestMappingMyController可能需要被一些自定义注释替换(并适应findMethodWithRequestMapping对此自定义注释执行自省)。

于 2013-01-23T16:57:56.843 回答