Fork me on GitHub
ASP.NET MVC 4 小项目开发总结

ASP.NET MVC 4 小项目开发总结

项目很小,就是一个企业站的前后台,主要包括新闻模块、产品模块、视频模块、留言。没有什么技术上的难点,大部分就是CRUD操作。开始之前评估开发时间为4天,实际coding时间为3天,debug时间为2天,关于debug时间较长的问题,后面有较为详细的分析。

所用技术和工具

  • Visual Studio 2012
  • ASP.NET MVC 4
  • Entify Framework 5
  • Sqlite
  • Uploadify

关于ASP.NET MVC 4

相比MVC 3,个人感觉并没有太大的变化,也许是一些新特性没有用到。

debug花费时间分析

除开没有后台页面等其他因素,自身原因分析如下:

拿到需求后没有进行较为详细的确认

虽然项目需求简单,但有些地方开始时疏于沟通,最终所省掉的时间还是用在了debug上,甚至更多。

浏览器兼容性问题

仅在开发阶段使用chrome浏览器,ie系也仅测试ie10,其余未考虑,后续在浏览器兼容性方面的调试花费了较多的时间。

例如如下的问题:

  • ie7下jquery.validate报错

    this.attr("novalidate", "novalidate"); 修改为:if (typeof (Worker) !== "undefined") { this.attr('novalidate', 'novalidate'); }

  • ie7下ckeditor的dialog加载iframe窗口高度不正常问题

    尝试了很多网上的解决方案,均不管用,后来使用了一个非常规手段,就是给浏览器加上强制使用ie8模式的meta信息

  • ie8下jquery.validate不起作用

    版本匹配问题:经测试:jquery-1.8.2 with jquery.validate-1.9正常

uploadify控件使用不是很熟练

很多api需要现查官方文档,而且官方站点还需FQ。同时在集成uploadify到ckeditor里面的时候,也花费了较多的时间,主要是用在查官方文档上面。这块写了较多的js代码,在后续浏览器兼容性方面调试也比较麻烦。

  • firefox下上传文件出现http error 302

    网上大部分的情况是firefox和chrome同时出现此问题,基本都是说session的原因,但我的环境chrome却没有出问题。我的解决方案比较简单,就是对上传文件的后台action取消授权检查。应该还是跟session有关,更好的解决方案可查询谷歌。

Entity Framework sqlite数据源适配问题

主要是开始无法新建sqlite数据源,ef的设计器总是报错,无法通过数据库更新实体等。另外sqlite中文模糊查询问题。

    • 设计器报错问题

      需要到sqlite官网下载合适的数据源驱动程序http://system.data.sqlite.org/index.html/doc/trunk/www/downloads.wiki

    • sqlite中文字符串模糊查询问题

      原来使用的方式:dbcontext.Post.Where(t=>t.Name.Contains(s)),对应的sql语句为charindex,改为:list = context.Database.SqlQuery(string.Format("select * from product where name like '%{0}%'",arcTitle)).AsQueryable();

版权声明

 
分类: MVC

WebService深入剖析

最近做WebService,最开始用weblogic自带的集成IDE---Eclipse做,,死活出不来,很惨很惨,然后换了个myeclipse8.6来整,使用Tom猫部署。

基于JAX-WS做,然后使用SOAP消息调用WebService。整了1个多小时,坑。。。

虽然关于HelloWorld的例子满天飞,但是今天还是从该出起,完整的记录点点滴滴。

整体工程:

 

步骤:

一:首先加入JAX-WS核心包:

Book类为实体类,包含基本的ID,名称之类的。

以其中的UpdateBook作为例子:

二:创建BookService接口:

@WebService
@SOAPBinding(style=SOAPBinding.Style.RPC)
public interface UpdateBook {
    public String update(int id1, int id2, String name);
}

 

 三:创建BookService接口实现类:

 

复制代码
@WebService(endpointInterface = "com.bfchuan.server.UpdateBook")
public class UpdateBookImpl implements UpdateBook{

    @Override
    public String update(int id1, int id2, String name) {
        if(id1==1&id2==2&name.equals("bfc")){
            return "SUCC";
        }else{
            return "Fail";
        }
    }

}
复制代码

 

WebService为注解,该注解能设置很多选项:
- namewsdl:portType 的名称。缺省值为 Java 类或接口的非限定名称。(字符串)
- targetNamespace指定从 Web Service 生成的 WSDL 和 XML 元素的 XML 名称空间。缺省值为从包含该 Web Service 的包名映射的名称空间。(字符串)
- serviceName指定 Web Service 的服务名称:wsdl:service。缺省值为 Java 类的简单名称 + Service。(字符串)
- endpointInterface指定用于定义服务的抽象 Web Service 约定的服务端点接口的限定名。如果指定了此限定名,那么会使用该服务端点接口来确定抽象 WSDL 约定。(字符串)
- portNamewsdl:portName。缺省值为 WebService.name+Port。(字符串)
- wsdlLocation指定用于定义 Web Service 的 WSDL 文档的 Web 地址。Web 地址可以是相对路径或绝对路径。(字符串)


这些属性如果不设置,在WSDL里面会有默认值。当你每将一个类发布成服务的时候,就会有一个WSDL描述文件。

第四:增加WSDL文件,在该项目中,该文件如下:
复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is JAX-WS RI 2.1.3-hudson-390-. -->
<definitions xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://server.bfchuan.com/" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="UpdateBookImplService" targetNamespace="http://server.bfchuan.com/">
  <types/>
  <message name="update">
    <part name="arg0" type="xsd:int"/>
    <part name="arg1" type="xsd:int"/>
    <part name="arg2" type="xsd:string"/>
  </message>
  <message name="updateResponse">
    <part name="return" type="xsd:string"/>
  </message>
  <portType name="UpdateBook">
    <operation name="update" parameterOrder="arg0 arg1 arg2">
      <input message="tns:update"/>
      <output message="tns:updateResponse"/>
    </operation>
  </portType>
  <binding name="UpdateBookImplPortBinding" type="tns:UpdateBook">
    <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
    <operation name="update">
      <soap:operation soapAction=""/>
      <input>
        <soap:body namespace="http://server.bfchuan.com/" use="literal"/>
      </input>
      <output>
        <soap:body namespace="http://server.bfchuan.com/" use="literal"/>
      </output>
    </operation>
  </binding>
  <service name="UpdateBookImplService">
    <port binding="tns:UpdateBookImplPortBinding" name="UpdateBookImplPort">
      <soap:address location="http://localhost:8080/WebService/UpdateBookImplPort"/>
    </port>
  </service>
</definitions>
复制代码

现在为WSDL做一一的解析:

1.Type:

其中Type为空,为什么呢?

因为Type是用来导入和局部定义web服务交换信息要使用的XML Schema中的简单类型定义-------所有的Web服务将能够随时使用它们,因此,在这个项目中没有必要在这此处放入任何东西。通常来说,WSDL将Type定义成指向外部的schema。

举个例子,一个需要使用Type的格式如下所示:

<type>
<xsd:Schema>
        <xsd:import namespace=http://soacookbook.com/
            SchemaLocation-"http://localhost:7777/Test/HelloWSService?sxd=1"/>
</xsd:Schema>
</type>

 

2.Message

WSDL的Message部分包含请求和响应定义,与服务器通信时,要使用的这些请求和响应,你可能会注意到,请求和响应都是各自对应一条信息。请求信息与方法调用名称相同,因此此处采用的是RPC样式。响应名称是按照在操作后附加“Response”的方式给出的,这是JAX-WS的规范的默认方式。

对于请求:

<message name="update">
    <part name="arg0" type="xsd:int"/>
    <part name="arg1" type="xsd:int"/>
    <part name="arg2" type="xsd:string"/>
 </message>

每一个消息,都包含一个part子元素,消息part与方法中的参数类似,具有名称和类型。

第一个part的名称是arg0,后面的以此类推,这个名字也是客户端调用的时候需要传递参数时候使用的名称(针对SOAP消息调用服务,后面会说到)

当然这些名称是系统默认的,你可以对它进行定制。

对于响应:

<message name="updateResponse">
    <part name="return" type="xsd:string"/>
 </message>

由于默认参数的样式纸是:wraped,Web服务返回值将封装在单个元素中,名称为return,你可以通过它提取剩余的载荷。

3.Binding

WSDL的Binding部分指定于服务器之间的消息传递方式。默认是SOAP,其他高级的选项包括HTTP,JSM,SMTP和TCP。由于通过对UpdateBook接口使用了这个注释:

@SOAPBinding(style=SOAPBinding.Style.RPC)

指定希望将服务绑定带SOAP,因此会获得SOAP绑定作为消息传输方式

<soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>

这里指定了一个地址,这意味着服务将使用SOAP1.1作为消息的发送和接受的协议。

还有个属性:style=“rpc”这个,该WSDL编写它是因为这个接口存在以上的那个注解。

4.Service

WSDL的Service部分指定服务名称将是UpdateBookImplService 。该名称是在定义服务器的类名称后面加上Service的,这是JAX-WS在没有自定义名字的时候采用的默认命名。

这儿还指定使用SOAP绑定UpdateBookImplPort端口,端口命名也是默认,规则你懂的。

该部分带有SOAP的前缀,说明他们来自:"http://schemas.xmlsoap.org/wsdl/soap/"命名空间。

此处的Location属性指定可以在什么位置调用该服务,以及将使用哪些客户端来调用。

第五:创建sun-jaxws.xml文件,该文件如下所示:

复制代码
<?xml version = "1.0"?>
<endpoints version="2.0"
    xmlns="http://java.sun.com/xml/ns/jax-ws/ri/runtime">
    <endpoint name="UpdateBookImplPort"
        implementation="com.bfchuan.server.impl.UpdateBookImpl"
        url-pattern="/UpdateBookImplPort">
    </endpoint>
</endpoints>
复制代码

该文件中,定义了该实现类的访问端口UpdateBookImplPort,即当访问该端口的时候,去找com.bfchuan.server.impl.UpdateBookImpl这儿。

第七:配置Web.xml文件

相信这个文件,都不陌生。该文件如下所示:

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 
    xmlns="http://java.sun.com/xml/ns/javaee" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
    http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
  <servlet>
      <description>JAX-WS endpoint - HelloImplService</description>
      <display-name>HelloImplService</display-name>
      <servlet-name>HelloImplService</servlet-name>
    <!--配置监听--->
<servlet-class> com.sun.xml.ws.transport.http.servlet.WSServlet </servlet-class> <load-on-startup>1</load-on-startup> </servlet>
  <!--这儿的UpdateBookImplPort就是对应sun-jaxws.xml中的UpdateBookImplPort,当访问这个UpdateBookImplPort的时候由
    这儿转向sun-jaxws.xmlUpdateBookImplPort,再由UpdateBookImplPort去找实现类-->
<servlet-mapping> <servlet-name>HelloImplService</servlet-name> <url-pattern>/UpdateBookImplPort</url-pattern> </servlet-mapping>
<welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <listener> <listener-class> com.sun.xml.ws.transport.http.servlet.WSServletContextListener </listener-class> </listener></web-app>
复制代码

于是整个WebService的文件解析完了。

流程为:

首先web.xml接收到请求port的请求------然后在sun-jaxws.xml中找对应的port-----找到对应的实现类

为什么他能找到?WSDL在这儿起了管理作用,定义了相关信息,将某个port和某个service绑定在一起。

然后就是客户端。客户端我采用SOAP消息调用service的服务。

现在对SOAP的消息做一个介绍:

SOAP基于HTTP协议传输,(通常是这样,当然也可一使用JSM和其他机制)。合并防火墙通常允许交换已有HTTP流量,并使用适当机制来处理流量,包括防火墙规则,分支网络等。

SOAP被创建用来特定满足基于SOAP的WEB服务开发新手的需要,他可以手动操纵SOAP信封。关于SOAP信封的消息,度娘很多了。

下面基于该例子做一个基于SOAP消息调用服务:

一:首先创建一个java工程:

第二:创建一个文件,名字为:book.msg

内容为:

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soap:Body>
<ns1:update xmlns:ns1="http://server.bfchuan.com/">
<arg0>3</arg0>
<arg1>3</arg1>
<arg2>bfc</arg2>
</ns1:update>
</soap:Body>
</soap:HelloImpl>
复制代码

这儿的arg0分别对于WSDL里的那个参数,还记得吧。

这里的update对应WSDL里的操作数。

其他的为SOAP的消息体。

SOAP的消息载体写好了,现在我们需要发送消息。

第三:编写java类发送SOAP消息:

复制代码
public class UpdateBook {

    public static void main(String[] args) {
        doSoapPost();
    }
    
    
    public static void doSoapPost()
    {
        try 
        {
             //First create the connection
             SOAPConnectionFactory soapConnFactory = 
                                SOAPConnectionFactory.newInstance();
             SOAPConnection connection = 
                                 soapConnFactory.createConnection();
             
             //Next, create the actual message
             MessageFactory messageFactory = MessageFactory.newInstance();
             SOAPMessage message = messageFactory.createMessage();
             
             //Create objects for the message parts            
             SOAPPart soapPart = message.getSOAPPart();
             SOAPEnvelope envelope = soapPart.getEnvelope();
             SOAPBody body = envelope.getBody();       
             
            //Populate the Message
            StreamSource preppedMsgSrc = new StreamSource( 
                     new FileInputStream("msg/updatebook.msg"));
            soapPart.setContent(preppedMsgSrc);
             //Save the message
             message.saveChanges();
             //Check the input
             System.out.println("/nREQUEST:/n");
             message.writeTo(System.out);
             System.out.println();
            //Send the message and get a reply   
                
            //Set the destination
            String destination = 
                  "http://localhost:8080/WebService/UpdateBookImplPort";
            //Send the message
            SOAPMessage reply = connection.call(message, destination);
            
//          Check the output
            System.out.println("/nRESPONSE:/n");
            //Create the transformer
            TransformerFactory transformerFactory = 
                               TransformerFactory.newInstance();
            Transformer transformer = 
                            transformerFactory.newTransformer();
            //Extract the content of the reply
            Source sourceContent = reply.getSOAPPart().getContent();
            //Set the output for the transformation
            StreamResult result = new StreamResult(System.out);
            transformer.transform(sourceContent, result);
             //Close the connection            
             connection.close();
        } 
        catch(Exception e) 
        {
                System.out.println(e.getMessage());
        }   
    }


}
复制代码

对于该代码不做过多的说明,当该程序运行后,结果如下所示:

复制代码
/nREQUEST:/n
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soap:Body>
<ns1:update xmlns:ns1="http://server.bfchuan.com/">
<arg0>2</arg0>
<arg1>2</arg1>
<arg2>"bfc"</arg2>
</ns1:update>
</soap:Body>
</soap:HelloImpl>


/nRESPONSE:/n

<?xml version="1.0" encoding="UTF-8"?><S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<
S:Body><ns2:updateResponse xmlns:ns2="http://server.bfchuan.com/">
  <
return>Fail</return>
  </
ns2:updateResponse>
</
S:Body>
</
S:Envelope>
复制代码

前面是发送的SOAP消息

后面是服务器返回的SOAP消息

如果说你返回的的是个对象,比如查询某个Book,那么返回的服务的SOAP是如下所示:

复制代码
<?xml version="1.0" encoding="UTF-8"?>

<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
  <
S:Body>
    <
ns2:byBookResponse xmlns:ns2="http://server.bfchuan.com/">
        <
return><desc>BBB</desc>
        <
id>2</id>
        <
name>AAA</name>
        </
return>
    </
ns2:byBookResponse>
  </
S:Body>
</
S:Envelope>
复制代码

该SOAP返回的是一个对象。

最后题外话:

对于WebService的架构方式,有JAX-WS还有Xfire。对此做个比较:

 

1)   用基于jax-wsClient端调用基于XFirejax-wsWebService都没有问题;

而用基于XFireClient端调用基于XFireWebService存在问题(什么原因目前还不明);

2)   基于Jax-wsClient端只能通过解析WSDL文档的方式来调用WebService,不可以使用将WebService的接口抓到本地进行掉用的方式;

基于XFireClient端则能够通过两种方式来调用WebService:(但目前这两种方式只针对XFire发布的WebService

(1)       WebServiceInterface抓到本地,进行调用;

(2)       通过得到WebServiceWSDLFile或是WSDLURL,解析WSDL来调用WebService

3)   基于jax-wsWebServiceClient端可以建立在任何Project中;

基于XFireWebServiceClient端则必须建立在WebServiceProject中;

对于非WebServiceProjectProject要想调用基于XFireWebService必须将其Interface抓到本地进行调用;

4)   基于Jax-ws发布的WebService只发布WSDL文档,

基于XFireWebService则会发布WSDl+服务接口;

5)   基于Jax-ws发布的WebService会显示的产生WSDL文档;

基于XFire发布的WebService则只是在调用时由服务器产生临时的;

   6)Jax-ws设计了监听器机制,在WEB.xml中已经能看到监听类的配置了XFire没有;

 

 

 
 
分类: 程序员
posted on 2013-06-15 18:39  HackerVirus  阅读(382)  评论(0编辑  收藏  举报